﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace PowerLife
{
    class WorldGen
    {
        private int worldWidth; //worldwidth in chunks
        private int worldHeight; //worldheight in chunks
        private int chunkSize;
        private float averageLandHeight;
        private float seaLevel;
        private float terrainNoise;
        Random randGen;
        private Node[,] WorldGrid;

        public WorldGen(int worldWidth, int worldHeight, int chunkSize, float avgLandHeight, float seaLevel, float terrainNoise)
        {
            this.worldWidth = worldWidth;
            this.worldHeight = worldHeight;
            this.chunkSize = chunkSize;
            this.averageLandHeight = avgLandHeight;
            this.seaLevel = seaLevel;
            this.terrainNoise = terrainNoise;
        }




        private class ColumnGenReturnData
        {
            public int xLocLeft1, xLocLeft2, xLocRight1, xLocRight2;
            public float xHeightLeft1, xHeightLeft2, xHeightRight1, xHeightRight2;

            public ColumnGenReturnData(int xLocLeft1, int xLocLeft2, int xLocRight1, int xLocRight2, float xHeightLeft1, float xHeightLeft2, float xHeightRight1, float xHeightRight2)
            {
                this.xLocLeft1 = xLocLeft1;
                this.xLocLeft2 = xLocLeft2;
                this.xLocRight1 = xLocRight1;
                this.xLocRight2 = xLocRight2;
                this.xHeightLeft1 = xHeightLeft1;
                this.xHeightLeft2 = xHeightLeft2;
                this.xHeightRight1 = xHeightRight1;
                this.xHeightRight2 = xHeightRight2;
            }
        }


        //call to generate a new world and create a world file
        public void generate(string worldName, int seed)
        {
            randGen = new Random(seed);
            WorldGrid = new Node[worldWidth * chunkSize, worldHeight * chunkSize];

            string mydocpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

           

            if (File.Exists( mydocpath + "\\" + worldName + ".world"))
            {
                return;
            }
        
            //for x in 0 to worldWidth*chunkSize
            Queue<int> xLocLeft = new Queue<int>();
            Queue<int> xLocRight = new Queue<int>();
            Queue<float> xHeightLeft = new Queue<float>();
            Queue<float> xHeightRight = new Queue<float>();
            
            
            xLocLeft.Enqueue(0);
            xLocRight.Enqueue(worldWidth * chunkSize);

            xHeightLeft.Enqueue(averageLandHeight);
            xHeightRight.Enqueue(averageLandHeight);

            for (int i = 0; i < (int)averageLandHeight; i++)
            {
                if (seaLevel < i)
                {
                    WorldGrid[0, i] = new Node(2);
                    WorldGrid[worldWidth * chunkSize - 1, i] = new Node(2);
                }
                else
                {
                    WorldGrid[0, i] = new Node(0);
                    WorldGrid[worldWidth * chunkSize - 1, i] = new Node(0);
                }

                
            }
            //fill ground nodes
            for (int i = worldHeight*chunkSize-1; i >= (int)averageLandHeight; i--)
            {
                WorldGrid[0, i] = new Node(1);
                WorldGrid[worldWidth * chunkSize - 1, i] = new Node(1);

            }

            
            while (xLocLeft.Count > 0 )
            {
                ColumnGenReturnData ret = genColumn(xLocLeft.Dequeue(), xLocRight.Dequeue(), xHeightLeft.Dequeue(), xHeightRight.Dequeue());

                //else add to lists


                //if newxleft1 = -1; skip
                if (ret.xLocLeft1 != -1)
                {
                    xLocLeft.Enqueue(ret.xLocLeft1);
                    xLocRight.Enqueue(ret.xLocRight1);
                    xHeightLeft.Enqueue(ret.xHeightLeft1);
                    xHeightRight.Enqueue(ret.xHeightRight1);
                }
                //if newxleft2 = -1; skip
                if (ret.xLocLeft2 != -1)
                {
                    xLocLeft.Enqueue(ret.xLocLeft2);
                    xLocRight.Enqueue(ret.xLocRight2);
                    xHeightLeft.Enqueue(ret.xHeightLeft2);
                    xHeightRight.Enqueue(ret.xHeightRight2);
                }

            }

            writeWorld(worldName);

        }


        public void printGrid()
        {
            for (int y = 0; y < worldHeight*chunkSize; y++)
            {
                System.Diagnostics.Debug.Write("Row " + y + ": ");
                for (int x = 0; x < worldWidth*chunkSize; x++)
                {
                    System.Diagnostics.Debug.Write(WorldGrid[x,y].GetNodeType() + " ");

                }
                System.Diagnostics.Debug.Write("\n");
            }

        }
        //saves new world to file


        private void writeWorld(string worldname)
        {


            string mydocpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

            using (StreamWriter outfile = new StreamWriter(mydocpath + "\\" + worldname + ".world")) 
            {
                outfile.Write(worldWidth.ToString() + "\n");
                outfile.Write(worldHeight.ToString() + "\n");
                outfile.Write(chunkSize.ToString() + "\n");
                StringBuilder sb = new StringBuilder();

                for (int y = 0; y < worldHeight * chunkSize; y++)
                {
                    for (int x = 0; x < worldWidth * chunkSize; x++)
                    {
                        sb.Append(WorldGrid[x, y].GetNodeType().ToString());
                    }
                    outfile.Write(sb.ToString() + "\n");
                    sb.Clear();
                }
            }
            //StreamWriter writer = new StreamWriter(exepath);
        }

        private ColumnGenReturnData genColumn(int xLocLeft, int xLocRight, float xHeightLeft, float xHeightRight)
        {

            //take average of sides
            //add random rise or fall based on width and roughness

            float newHeight = (xHeightLeft + xHeightRight) / 2.0F + ((float)randGen.NextDouble() - 0.5f)*((xLocRight - xLocLeft)*terrainNoise);
            //center rounded down
            int centerLoc = (xLocRight-xLocLeft)/2 + xLocLeft;

            if (newHeight < 0)
                newHeight = 0;
            if (newHeight > worldHeight * chunkSize)
                newHeight = worldHeight * chunkSize - 1;

            WorldGrid[centerLoc, (int)newHeight] = new Node(1);

            //fill sky nodes
            for (int i = 0; i < (int)newHeight; i++ )
            {
                if (seaLevel < i)
                {
                    WorldGrid[centerLoc, i] = new Node(2);
                }
                else
                {
                    WorldGrid[centerLoc, i] = new Node(0);
                }
            }
            //fill ground nodes
            for (int i = worldHeight*chunkSize-1; i > (int)newHeight; i--)
            {

                WorldGrid[centerLoc, i] = new Node(1);
            }
            //return two new divisions, if new division has at least 1 space between it
            int newXLeft1 = xLocLeft;
            int newXLeft2 = centerLoc;
            int newXRight1 = centerLoc;
            int newXRight2 = xLocRight;
            float newXLeft1Height = xHeightLeft;
            float newXLeft2Height = newHeight;
            float newXRight1Height = newHeight;
            float newXRight2Height = xHeightRight;

            if (newXRight1 - newXLeft1 <= 1)
                newXLeft1 = -1;
            if (newXRight2 - newXLeft2 <= 1)
                newXLeft2 = -1;


            return new ColumnGenReturnData(newXLeft1, newXLeft2, newXRight1, newXRight2, newXLeft1Height, newXLeft2Height, newXRight1Height, newXRight2Height);
        }

    }
}
